#ifndef FKROOM_H
#define FKROOM_H

#include "FKObject.h"

#include <QVariant>

#include "FKActorsModel.h"
#include "FKFileInfo.h"

#include "FKRoomStates.h"

class FKUser;
class FKActor;
struct FKFileInfo;
class QTimer;

class FKWorldReferent;
class FKRoomInterface;

class FKRoomModule{
public:
    virtual ~FKRoomModule(){}
    virtual QString roomType()const=0;
    virtual QString moduleClass()const=0;
    virtual QString roomClass()const=0;
    virtual void loadModule()const=0;
    virtual FKFileInfo version()const=0;
};

#define FKRoomModule_IID "rooms.plugins"
Q_DECLARE_INTERFACE(FKRoomModule,FKRoomModule_IID)

#define FK_ROOM_MODULE(RoomType,RoomClass,ModuleClass,Version) \
class ModuleClass:public QObject,FKRoomModule{\
    Q_OBJECT\
    Q_PLUGIN_METADATA(IID FKRoomModule_IID)\
    Q_INTERFACES(FKRoomModule)\
    virtual QString roomType()const{return QString(tr(RoomType));}\
    virtual QString moduleClass()const{return metaObject()->className();}\
    virtual QString roomClass()const{return RoomClass::staticMetaObject.className();}\
    virtual void loadModule()const{FKSystemObject::addClass<RoomClass>();regClasses();}\
    void regClasses()const;\
    virtual FKFileInfo version()const{FKFileInfo i=FKFileInfo::makeLibrarySet(QString(RoomType),QString(Version));\
                i.relativePath+=("rooms/");\
                return i;\
    }\
};\
inline void ModuleClass::regClasses()const

class FKOBJECTSSHARED_EXPORT FK_OBJECT(FKRoom,FKObject)
    Q_OBJECT
    Q_PROPERTY(QString cn READ roomName WRITE setRoomName)
        //C_name
    Q_PROPERTY(QStringList cmh READ messageHistory WRITE e_setMessageHistory)
        //C_messageHistory
public:
//    FKRoom(qint32 objectID=-1);
//    FKRoom(qint32 dummy=0, qint32 objectID=-1);
        //forbidden constructor, used for auto-constructing only
//    ~FKRoom();
    QStringList messageHistory() const;
        //get public message history (last _messageHistoryCapacity messages)
    void setChatLogFile(const QString &file){_chatLogFile=file;}
        //set absolute path for file, where chat log write to
    void enqueEvent(FKEvent* event);
        //enque event for processing in room thread
    void enqueAction(FKEvent* action);
        //enque action for processing in room thread
    void addObject(FKObject* obj);
        //add object to room's objects, used by FKObject constructor
    void addUser(FKUser* user);
        //add User to room
    void removeObject(FKObject* obj);
        //unset room for object obj
    bool hasUser(FKUser* user);
        //check for user already entered room
    void removeUser(FKUser* user);
        //remove user from room. Empty rooms autodeleting after deleteTimeOut ms
    void userConnected(FKUser* user);
        //notify room user was (re)connected
    void userDisconnected(FKUser* user);
        //notify room user was disconnected
    void reconnectClient(FKObject* client, const QList<FKUser*>& users);
        //join client back to dropped loaded room
    void refreshActorsModel(FKObject* client);
        //refresh actors model at user side
    FKActorsModel* actorsModel() const;
        //return used actors model
    QString roomName() const;
        //return displaying name of room
    virtual QString roomType() const{return QString("FKRoom");}
        //Used for displaying room's type
    virtual FKRoomInterface* interfaceClass() const{return 0;}
        //Return className of used interface class. Used by FKUserInterface to create interface for room
    virtual FKWorldReferent* worldClass()const{return 0;}
        //Return className of used world class. Used by FKUserInterface to create world for room
    virtual void connectInterface(QObject* roomInterface);
        //This function used by FKUserInterface for connecting room and its interface.
        //Room can recieve internal events that influence interface and should notify interface to change
        //Both located in different modules and should be connected via SIGNALS-SLOTS
    bool started() const{return _state!=FKRoomStates::nonLoaded;}
        //used for determining already started rooms
    static QList<QMap<int, QVariant> > roomList();
        //retrive informaniton about avaliable rooms for displaying lists
    static FKRoom* createRoom(QStringList roomData);
        //broken function. Should be reworked. Create room from roomData argument
    void setActive(const bool &t);
        //let room objects recieve custom values
    bool isActive()const{return !servant || servant->isActive;}
        //return true for client-side rooms and for active server-side rooms
    static void setAvaliableRoomModules(const QMap<QString,FKRoomModule*>& m){_avaliableModules=m;}
    static void initRoomModule(const QString& roomClass);
protected:
    void setModel(FKActorsModel* model);
    QList<FKUser*> _users;
    QList<FKActor*> _actors;
    static const qint32 deleteTimeOut;
    virtual void loadActors();
signals:
    void recievedEvent(FKEvent* event);
    void recievedAction(FKEvent* action);
    void gotPublicMessage(QString message);
    void loadSceneRequested();
    void loadingBegan(int p1, int p2);
    void loadingProgress(int p1, int p2);
    void loadingEnded();
    void loadingComplete();
    void messageHistoryUpdated(QStringList history);
    void recalculate();
    void actorMessage(QString msg);
//    void userGone(FKUser* user);
private slots:
    void manageAction(FKEvent* action);
    void manageEvent(FKEvent* event);
//    void internalSound(qint32 sample, qint32 volume);
    void actorsModelDataChanged(FKModelData d);
    void sceneLoaded();
    virtual void actorWin(FKActor* actor);
    virtual void actorLose(FKActor* actor);
private:
//    virtual void userGone(FKUser* user);
    void setRoomName(QString roomName);
    void E_publicMessage(FKEvent* event);
    void writeChatLog(const QString &msg);

    void E_win(FKEvent* event);
    void E_lose(FKEvent* event);

//    void E_addActor(FKEvent* event);

    void A_beginLoading(FKEvent* event);
    void E_beginLoading(FKEvent* event);
    virtual bool startCondition(){return true;}
    void E_loadScene(FKEvent *event);
    virtual void loadCore();
    virtual void loadUser(FKUser* user);
    void E_loadingProgress(FKEvent* event);
    virtual void initStart();
    void E_loadingComplete(FKEvent* event);
    void initObjects();
    void A_clientLoadingComplete(FKEvent* event);
    void clientLoadingComplete();
//    void A_sceneLoadingComplete(FKEvent* event);
    void E_endLoading(FKEvent *event);

    void E_updateActorsModel(FKEvent* event); //update 1 record value
    void E_reinstallActorsModel(FKEvent* event); //update full data structure
//    void E_setMessageHistory(FKEvent* event){e_setMessageHistory(event->value().toStringList());}
    void e_setMessageHistory(QStringList history);
    void coreLoading();
//    void E_setRoomName(FKEvent* event){setRoomName(event->value().toString());}
//    FKActorsModelData emptyAMD(){return FKActorsModelData();}
//    QStringList initActionList();
//    QStringList initEventList();
//    QObject* _scene;
    QString _name;
    qint8 _state;
    bool _sceneLoaded;
    //    QThread* _thread;
        QHash<qint32,FKObject*> _objects;
        FKActorsModel* _actorsModel;
        QTimer* _deleteTimer;
        QStringList _messageHistory;
//    QList<FKActorSlot*> _freeActorSlots;
    static QLinkedList<FKRoom*> _roomsExist;
    static QMap<QString,FKRoomModule*> _avaliableModules;

    QString _chatLogFile;

//    FKWorldReferent* _worldReferent;
//    bool _started;
    static const int _messageHistoryCapacity;

    struct Servant{
        Servant():isActive(false){}
        QMap<qint32,qint8> loadingClients;
        bool isActive;
    };

    FK_NO_CUSTOM_VALUES
};

#endif // FKROOM_H
